package kb;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import utils.io.KBAccess;


import entities.Assertion;
import entities.ConceptAssertion;
import entities.Individual;
import entities.RoleAssertion;
import entities.concept.AndConcept;
import entities.concept.Concept;
import entities.role.Role;
import entities.vocabulary.Display;

public final class ABox {
	
	private HashMap<Individual,Concept> indexConceptAssertions;
	private HashMap<Individual, HashMap<Role, HashSet<Individual>>> indexRoleAssertions;
	private HashSet<Individual> allABoxIndividuals;
	private int numofCA, numofRA;
	
	public void setNums(int ca, int ra){
		this.numofCA = ca;
		this.numofRA = ra;
	}
	
	public int getNumCA(){
		return this.numofCA;
	}
	public int getNumRA(){
		return this.numofRA;
	}
	public void cleanUp(){
		//clean all individuals in this abox, as they may have been changed in previous reasoning sessions
		for(Individual ind:this.allABoxIndividuals){
			ind.restore();
		}
	}
	
	public ABox deepcopyOfThisABox(){
		ABox ab = new ABox();
		ab.allABoxIndividuals.addAll(this.allABoxIndividuals);
		ab.indexConceptAssertions.putAll(this.indexConceptAssertions);
		ab.indexRoleAssertions.putAll(this.indexRoleAssertions);
		return ab;
	}
	
	/*
	 * Given *:C for an abox, the abox populates this star assertion and 
	 * return all reified assertions.
	 */
	public List<ConceptAssertion> populateStarConceptAssertion(ConceptAssertion spec){
		List<ConceptAssertion> res = new LinkedList<ConceptAssertion> ();
		Concept c = spec.getConcept();
		for(Individual ind:this.getABoxIndividuals()){
			res.add(new ConceptAssertion(ind, c));
		}
		return res;
	}
	
	//add a file of assertions to an existing ABox
	public static ABox addToABox(ABox ab, String file){
		HashSet<Assertion> toadd = null;
		try {
			toadd = KBAccess.processAssertionFile(new BufferedReader(new FileReader(file)), false);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//index all assertions here
		ab.insertAssertions(toadd);
		return ab;
	}
	
	public HashMap<Individual,Concept> getConceptAssertions(){
		return this.indexConceptAssertions;
	}
	
	public HashMap<Individual, HashMap<Role, HashSet<Individual>>> getRoleAssertions(){
		return this.indexRoleAssertions;
	}
	
//	public HashMap<String, Individual> getAllNamedIndividuals(){
//		return allIndividuals;
//	}
	
	//get data
	public Concept getConcept(Individual a){
		return this.indexConceptAssertions.get(a);
	}
	
	public HashSet<Individual> getABoxIndividuals(){
		return this.allABoxIndividuals;
	}
	
	public ABox(){
		this.indexConceptAssertions = new HashMap<Individual,Concept>();
		this.indexRoleAssertions = new HashMap<Individual,HashMap<Role, HashSet<Individual>>>();
		this.allABoxIndividuals = new HashSet<Individual>();
	}
	
	public boolean isRoleAssertionFreeABox(){
		return this.indexRoleAssertions.size()<1;
	}
	
//	public void insertIndividuals(Individual ind){
//		allIndividuals.put(ind.getName(), ind);
//	}
	
	public void insertConceptAssertions(ConceptAssertion ca){
		if(ca == null){
			System.err.println("Null concept assertions given in "+this.getClass().getName());
			return;
		}
		Individual ind = ca.getIndividual();
		this.allABoxIndividuals.add(ind);
//		this.insertIndividuals(ind);
		Concept c = ca.getConcept();
		if(c.isTop()){
			//a:T, no need to add for individual a.
			return;
		}
		if(this.indexConceptAssertions.containsKey(ind)){
			//conjoin the concept desc		 			
			c = new AndConcept(c, this.indexConceptAssertions.get(ind));
		}
		this.indexConceptAssertions.put(ind, c);				
	}
	
	public void rmConceptAssertions(ConceptAssertion ca){
		if(ca == null){
			System.err.println("Null concept assertions given in "+this.getClass().getName());
			return;
		}
		Individual ind = ca.getIndividual();
		//this.allABoxIndividuals.remove(ind);
		if(this.indexConceptAssertions.containsKey(ind)){
			Concept ic = this.indexConceptAssertions.get(ind);
			if(ic instanceof AndConcept){
				((AndConcept) ic).getConceptSet().remove(ca.getConcept());
			}
		}
	}
	
	public void insertAssertions(Set<Assertion> ax){
		if(ax != null){
			for(Assertion as:ax){
				insertAssertions(as);
			}
		}
	}
	
	public void insertAssertions(Assertion ast){
		if(ast instanceof ConceptAssertion){
			this.insertConceptAssertions((ConceptAssertion)ast);
		}else if(ast instanceof RoleAssertion){
			this.insertRoleAssertions((RoleAssertion)ast);
		}else{
			System.err.println("Assertion type unknown: "+ast);
		}
	}
	
	public void rmAssertions(Assertion ast){
		if(ast instanceof ConceptAssertion){
			this.rmConceptAssertions((ConceptAssertion)ast);
		}else if(ast instanceof RoleAssertion){
			this.rmRoleAssertions((RoleAssertion)ast);
		}else{
			System.err.println("Assertion type unknown: "+ast);
		}
	}
	
	
	private void insertRoleAssertions(RoleAssertion ra){
		Individual a = ra.getFirstIndividual();
		Individual b = ra.getSecondIndividual();
		this.allABoxIndividuals.add(a);
		this.allABoxIndividuals.add(b);
		Role r = ra.getRole();
		if(this.indexRoleAssertions.containsKey(a)){
			if(this.indexRoleAssertions.get(a).containsKey(r)){
				this.indexRoleAssertions.get(a).get(r).add(b);
			}else{
				// no such r.b
				HashSet<Individual> bs = new HashSet<Individual>();
				bs.add(b);
				this.indexRoleAssertions.get(a).put(r, bs);
			}
		}else{
			//no such a.r.b
			HashMap<Role, HashSet<Individual>> inner = new HashMap<Role, HashSet<Individual>>();
			HashSet<Individual> bs = new HashSet<Individual>();
			bs.add(b);
			inner.put(r, bs);
			this.indexRoleAssertions.put(a, inner);
		}
		return;
	}
	
	public void rmRoleAssertions(RoleAssertion ra){
		Individual a = ra.getFirstIndividual();
		Individual b = ra.getSecondIndividual();
		//this.allABoxIndividuals.remove(a);
		//this.allABoxIndividuals.remove(b);
		Role r = ra.getRole();
		if(this.indexRoleAssertions.containsKey(a)){
			if(this.indexRoleAssertions.get(a).containsKey(r)){
				this.indexRoleAssertions.get(a).get(r).remove(b);
				if(this.indexRoleAssertions.get(a).get(r).size()<1){
					this.indexRoleAssertions.get(a).remove(r);
					if(this.indexRoleAssertions.get(a).size()<1){
						this.indexRoleAssertions.remove(a);
					}
				}
			}
		}
		return;
	}

	@Override
	public String toString(){
		String ret = "";
		ret += "\n============\nAssertions in the ABox "+Display.ABOXUNICODE+":\n============\n";
		ret += "\n============Concept Assertions============\n";
		for(Entry<Individual, Concept> e:this.indexConceptAssertions.entrySet()){
			ret += e.getKey().toString()+" : "+ e.getValue().toString()+"\n";
		}
		ret += "\n============Role Assertions============\n";
		for(Entry<Individual, HashMap<Role, HashSet<Individual>>> e:this.indexRoleAssertions.entrySet()){
			for(Entry<Role, HashSet<Individual>> ee:e.getValue().entrySet()){
				for(Individual ind:ee.getValue()){
					ret += "("+e.getKey().toString()+", "+ind.toString()+") : "+ ee.getKey().toString()+"\n";
				}
			}
		}
		return ret;
	}
	
}
